local function Fn()
    local Fx = Class()


    function Fx:Init(NPC_BASE_LIB)
        ---------------------------------------------------------------------------------------------------------------------------

        function NPC_BASE_LIB:GetRandom(target,target2)
            if target and target2 and type(target) == "number" and type(target2) == "number" then
                return math.random(target, target2)
            end

            if target and type(target) == "table" then
                -- local x = math.floor( math.random()*100 )% #target + 1;
                -- return target[x]
                local num = math.random(#target)
                return target[num]
            end

            if target and type(target) == "number" and target >= 1 then
                local x = math.floor( math.random()*100 )% target + 1;
                return x
            end
        end

        function NPC_BASE_LIB:Get_Language()
            if type(TUNING.__NPC_LANGUAGE) == "function" then
                return tostring(TUNING.__NPC_LANGUAGE())
            elseif type(TUNING.__NPC_LANGUAGE) == "string" then
                return TUNING.__NPC_LANGUAGE
            else
                return "en"
            end

            -- local language = "en"
            -- pcall(function()
            --     language = TUNING.NPC_CONFIG.LANGUAGE
            --     if language == "auto" then
            --         if LOC.GetLanguage() == LANGUAGE.CHINESE_S or LOC.GetLanguage() == LANGUAGE.CHINESE_S_RAIL or LOC.GetLanguage() == LANGUAGE.CHINESE_T then
            --             language = "ch"
            --         elseif  LOC.GetLanguage() == LANGUAGE.JAPANESE then
            --             language = "jp"
            --         else
            --             language = "en"
            --         end
            --     end                
            -- end)
            -- return language
        end

        function NPC_BASE_LIB:Str_Split(str,delimiter)            
            -- local function split(input, delimiter)  
            --     input = tostring(input)
            --     delimiter = tostring(delimiter)
            --     if (delimiter == "") then return false end
            --     local pos, arr = 0, {}
            --     for st, sp in function() return string.find(input, delimiter, pos, true) end do
            --         table.insert(arr, string.sub(input, pos, st - 1))
            --         pos = sp + 1
            --     end
            --     table.insert(arr, string.sub(input, pos))
            --     return arr
            -- end
            if str == nil or type(str) ~= "string" or delimiter == nil or type(delimiter) ~= "string" then
                print("Error : Str_Split")
                return {}
            end
            local function mysplit (inputstr, sep)
                if sep == nil then
                    sep = "%s"
                end
                local t={}
                for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
                    table.insert(t, str)
                end
                return t
            end
            return mysplit(str,delimiter)
        end

        ---------------------------------------------------------------------------------------------------------------------------
        --- 坐标相关
        function NPC_BASE_LIB:Distance_Points(PointA,PointB)
            return ((PointA.x - PointB.x) ^ 2 + (PointA.z - PointB.z) ^ 2) ^ (0.5)
        end

        function NPC_BASE_LIB:Distance_Targets(tar1,tar2)
            return self:Distance_Points(Vector3(tar1.Transform:GetWorldPosition()), Vector3(tar2.Transform:GetWorldPosition()))
        end

        function NPC_BASE_LIB:Dsitance_T2P(tar,point)
            return self:Distance_Points(Vector3(tar.Transform:GetWorldPosition()),point)
        end

        function NPC_BASE_LIB:GetSpawnPoint(target, Distance)
            ----- target is NPC or Vectro3_point
            -- -- Create random refresh points
            local pt = nil
            if target.x == nil then
                pt = Vector3(target.Transform:GetWorldPosition())
            else
                pt = target
            end
    
            local theta = math.random() * 2 * PI
            -- local radius = math.random(20, 25)
            local radius = Distance
        
            local offset = FindWalkableOffset(pt, theta, radius, 12, true)
            if offset then
                return pt + offset
            else
                return nil
            end
        end

        function NPC_BASE_LIB:GetRandomPoint(caster, teleportee, target_in_ocean)
            local function getrandomposition(caster, teleportee, target_in_ocean)
                if teleportee == nil then
                    teleportee = caster
                end
        
                if target_in_ocean then
                    local pt = TheWorld.Map:FindRandomPointInOcean(20)
                    if pt ~= nil then
                        return pt
                    end
                    local from_pt = teleportee:GetPosition()
                    local offset = FindSwimmableOffset(from_pt, math.random() * 2 * PI, 90, 16)
                                    or FindSwimmableOffset(from_pt, math.random() * 2 * PI, 60, 16)
                                    or FindSwimmableOffset(from_pt, math.random() * 2 * PI, 30, 16)
                                    or FindSwimmableOffset(from_pt, math.random() * 2 * PI, 15, 16)
                    if offset ~= nil then
                        return from_pt + offset
                    end
                    return teleportee:GetPosition()
                else
                    local centers = {}
                    for i, node in ipairs(TheWorld.topology.nodes) do
                        if TheWorld.Map:IsPassableAtPoint(node.x, 0, node.y) and node.type ~= NODE_TYPE.SeparatedRoom then
                            table.insert(centers, {x = node.x, z = node.y})
                        end
                    end
                    if #centers > 0 then
                        local pos = centers[math.random(#centers)]
                        return Point(pos.x, 0, pos.z)
                    else
                        return caster:GetPosition()
                    end
                end
            end
            return getrandomposition(caster,teleportee,target_in_ocean)
        end

        function NPC_BASE_LIB:GetSurroundPoints(CMD_TABLE)
            -- local CMD_TABLE = {
            --     target = inst or Vector3(),
            --     range = 8,
            --     num = 8
            -- }
            if CMD_TABLE == nil then
                return
            end
            local theMid = nil
            if CMD_TABLE.target == nil then
                theMid = Vector3( self.inst.Transform:GetWorldPosition() )
            elseif CMD_TABLE.target.x then
                theMid = CMD_TABLE.target
            elseif CMD_TABLE.target.prefab then
                theMid = Vector3( CMD_TABLE.target.Transform:GetWorldPosition() )
            else
                return
            end
            -- --------------------------------------------------------------------------------------------------------------------
            -- -- 8 points
            -- local retPoints = {}
            -- for i = 1, 8, 1 do
            --     local tempDeg = (PI/4)*(i-1)
            --     local tempPoint = theMidPoint + Vector3( Range*math.cos(tempDeg) ,  0  ,  Range*math.sin(tempDeg)    )
            --     table.insert(retPoints,tempPoint)
            -- end
            -- --------------------------------------------------------------------------------------------------------------------
            local num = CMD_TABLE.num or 8
            local range = CMD_TABLE.range or 8
            local retPoints = {}
            for i = 1, num, 1 do
                local tempDeg = (2*PI/num)*(i-1)
                local tempPoint = theMid + Vector3( range*math.cos(tempDeg) ,  0  ,  range*math.sin(tempDeg)    )
                table.insert(retPoints,tempPoint)
            end

            return retPoints


        end
        ---------------------------------------------------------------------------------------------------------------------------
        
        function NPC_BASE_LIB:Say(talk_table,table_random)
            --     talk_table = string or { string,string,string }
            if talk_table and type(talk_table) == "table" and table_random == true then
                talk_table = self:GetRandom(talk_table)
            end

            if talk_table and type(talk_table) == "table" and #talk_table > 0 then
                -- -- self.inst.SoundEmitter:KillSound("talk2")
                -- -- self.inst.SoundEmitter:KillSound("talk")
                -- self.inst.components.talker:Say(tostring(talk_table.str))
                -- if self.inst.talk_sound and self.inst.SoundEmitter then
                --     self.inst.SoundEmitter:KillSound("talk")
                --     self.inst.SoundEmitter:PlaySound(self.inst.talk_sound,"talk2",1.5)
                --     self.inst:DoTaskInTime(2,function(inst)
                --         inst.SoundEmitter:KillSound("talk2")
                --     end)
                -- end

                local tempData = self.tempData
                local inst = self.inst

                if tempData.___talk_table_start ~= nil then
                    print("talk table is not end")
                    return
                end
                tempData.___talk_table_start = inst:DoTaskInTime(#talk_table*3 + 1,function()
                    tempData.___talk_table_start = nil
                end)
                for i, str in ipairs(talk_table) do
                    inst:DoTaskInTime( (i-1)*3,function()
                        self:Say(str)                        
                    end)
                end
            elseif talk_table and ( type(talk_table) == "string" or type(talk_table) == "number" ) then
                -- self.inst.SoundEmitter:KillSound("talk")
                -- self.inst.SoundEmitter:KillSound("talk2")
                if self.inst.components.talker then
                    self.inst.components.talker:Say(tostring(talk_table))
                end
                if self.inst.talk_sound and self.inst.SoundEmitter then
                    self.inst.SoundEmitter:KillSound("talk")
                       self.inst.SoundEmitter:PlaySound(self.inst.talk_sound,"talk2",1.5)
                    self.inst:DoTaskInTime(2,function(inst)
                        inst.SoundEmitter:KillSound("talk2")
                    end)
                end
                self.inst:PushEvent("npc_say",tostring(talk_table))
            end
            -- print(type(talk_table))
        end

        function NPC_BASE_LIB:Announce(Announce_table,random_flag)
            local tempData = self.tempData
            local inst = self.inst

            if Announce_table and type(Announce_table) == "string" then
                TheNet:Announce(Announce_table)
                return
            end

            if Announce_table and type(Announce_table) == "table" then
                if random_flag == true then
                    self:Announce(  tostring( self:GetRandom(Announce_table) )  )
                    return
                else
                    if tempData.__Announce_table_start ~= nil then
                        return
                    end
                    tempData.__Announce_table_start = self.inst:DoTaskInTime(#Announce_table*3+1,function()
                        tempData.__Announce_table_start = nil
                    end)
                    for i, str in ipairs(Announce_table) do
                        inst:DoTaskInTime( (i-1)*3.5,function()
                            inst.components.npc_base_lib:Announce(str)
                        end)
                    end

                end
            end
        end
        function NPC_BASE_LIB:Wisper(Announce_table,random_flag,list_str_flag)
            local tempData = self.tempData
            local inst = self.inst
            

            if Announce_table and type(Announce_table) == "string" then
                -- TheNet:Announce(Announce_table)
                -------------------------------------------------------------------
                --- 颜色
                local colors = {
                    {255/255,255/255,255/255},  --- 白色
                    {153/255,0/255,0/255},  --- 深红色
                    {255/255,0/255,127/255},    -- 紫红色
                    {153/255,255/255,51/255},   -- 浅绿色
                    {0/255,150/255,0/255},      -- 深绿色
                    {255/255,255/255,0/255},      -- 黄
                    {255/255,128/255,0/255},      -- 橙
                    {0/255,255/255,255/255},      -- 天蓝
                    {96/255,96/255,96/255},      -- 灰色
                }
                -------------------------------------------------------------------
                if tempData.__wishper_color == nil or list_str_flag == nil then
                    tempData.__wishper_color = self:GetRandom(colors)
                end
                -------------------------------------------------------------------
                self:RPC_PushEvent("npc_mod_event.whisper",{
                    message = tostring(Announce_table),
                    m_colour = tempData.__wishper_color,
                })
                -------------------------------------------------------------------
                return
            end

            if Announce_table and type(Announce_table) == "table" then
                if random_flag == true then
                    self:Wisper(  tostring( self:GetRandom(Announce_table) )  )
                    return
                else
                    if tempData.__Announce_table_start ~= nil then
                        return
                    end
                    tempData.__Announce_table_start = self.inst:DoTaskInTime(#Announce_table*3+1,function()
                        tempData.__Announce_table_start = nil
                    end)
                    for i, str in ipairs(Announce_table) do
                        inst:DoTaskInTime( (i-1)*3.5,function()
                            inst.components.npc_base_lib:Wisper(str,nil,true)
                        end)
                    end

                end
            end

        end
        ---------------------------------------------------------------------------------------------------------------------------
        function NPC_BASE_LIB:Set_Time_Out(num)
            if num and type(num) == "number" then
                self.tempData.__Time_Out_num = num
            end
        end
        function NPC_BASE_LIB:Get_Time_Out()
            return self.tempData.__Time_Out_num
        end
        ---------------------------------------------------------------------------------------------------------------------------
        function NPC_BASE_LIB:Get_Linked_Player()
            return self.tempData._Linked_Player or nil
        end

        function NPC_BASE_LIB:Set_Linked_Player(player)
            self.tempData._Linked_Player = player
        end

        function NPC_BASE_LIB:Add2Child(tempInst)
            if tempInst == nil then
                return
            end
            if self.inst.children == nil then
                self.inst.children = {}
            end
            self.inst.children[tempInst] = true

        end
        ---------------------------------------------------------------------------------------------------------------------------
        --- 装备皮肤修改 和穿戴 相关
        function NPC_BASE_LIB:Change_Item_Skin(arg1,arg2)
            if arg1 == nil then
                return
            end
            local doer = arg1
            local tarItem = arg2
            if arg2 == nil then
                tarItem = arg1
                doer = TheSim:FindFirstEntityWithTag("player")
            end

            if doer == nil or doer.userid == nil or tarItem == nil then
                return
            end

            ------------------------------------------------------------------------------------------------------
            ------------- Random Skin
            if not tarItem:HasTag("npc_no_random_skin") then  ---------------------------------------------------------------
                local function spellCB_Random(target,doer)
                    local function Skin_Change_test(doer,target)
                        
                        local function spellCB(doer, target)
                            local tool = target
                            if tool._cached_reskinname == nil then
                                tool._cached_reskinname = {}
                            end
                            -- if target.skins_table == nil then
                            --     target.skins_table = {}
                            -- end
                            local ret_skin_name = nil
                            local ret_skin_name2 =  nil
                            -- tool:DoTaskInTime(0, function()
                
                                local prefab_to_skin = target.prefab_base or target.prefab
                                if target:IsValid()  then
                                    local curr_skin = target.skinname
                                    local search_for_skin = tool._cached_reskinname[prefab_to_skin] ~= nil --also check if it's owned
                                    if curr_skin == tool._cached_reskinname[prefab_to_skin] or (search_for_skin and not TheInventory:CheckClientOwnership(doer.userid, tool._cached_reskinname[prefab_to_skin])) then
                                        local new_reskinname = nil
                
                                        if PREFAB_SKINS[prefab_to_skin] ~= nil then
                                            for _,item_type in pairs(PREFAB_SKINS[prefab_to_skin]) do
                                                if search_for_skin then
                                                    if tool._cached_reskinname[prefab_to_skin] == item_type then
                                                        search_for_skin = false
                                                    end
                                                else
                                                    if TheInventory:CheckClientOwnership(doer.userid, item_type) then
                                                        new_reskinname = item_type
                                                        break
                                                    end
                                                end
                                            end
                                        end
                                        tool._cached_reskinname[prefab_to_skin] = new_reskinname
                                    end
                                    
                                    local ret_1 = target.skinname
                                    local ret_2 = tool._cached_reskinname[prefab_to_skin]
                                    TheSim:ReskinEntity( target.GUID, target.skinname, tool._cached_reskinname[prefab_to_skin], nil, doer.userid )
                                    -- print("Change_Item_Skin",tool._cached_reskinname[prefab_to_skin])
                                    -- print("Change_Item_Skin",target.skinname)
                                    return ret_1,ret_2
                
                                    
                                end
                            -- end )
                
                            return nil,nil
                        end
                
                        ---------- 第一步，扫描所有拥有的皮肤,同时获得皮肤切换必须的参数 pre 和 tar
                        local skins_table_pre = {}
                        local skins_table_tar = {}
                        local count_num = 0
                        for i = 1, 10, 1 do
                            local ret1,ret2 =  spellCB(doer,target)
                            -- print("-------------",i,ret1,ret2)
                            if ret2 ~= nil then
                                skins_table_pre[i] = ret1
                                skins_table_tar[i] = ret2
                                count_num = count_num + 1
                            else
                                break
                            end
                        end
                        if count_num == 0 then
                            -- print("Error:no skins")
                            return 0
                        else
                            -- print("skin num : ",count_num)
                        end
                
                        -- for i = 1, count_num, 1 do
                        --     print(i,skins_table_pre[i],skins_table_tar[i])
                        -- end
                        ------------------------------------- 第二步，随机前往指定皮肤，必须 有 pre（当前） 和 tar 参数
                        local num = math.random(count_num)                
                        for i = 1, num, 1 do
                            TheSim:ReskinEntity( target.GUID, skins_table_pre[i], skins_table_tar[i], nil, doer.userid )
                            target.Change_Item_Skin_Build_name = skins_table_tar[i]
                        end
            
                        return count_num
                
                    end
                    if doer then
                        Skin_Change_test(doer,tarItem)
                    else
                        local temp_player = TheSim:FindFirstEntityWithTag("player")
                        if temp_player and temp_player.userid and temp_player.components.playercontroller then
                            Skin_Change_test(temp_player, tarItem)
                        end
                    end
                end
                local flag,error_code = pcall(spellCB_Random,tarItem,doer)
                if flag == false then
                    print("Error : Reskin Random")
                    print(error_code)
                end
            else      ------------------------------------------------------------------------------------------------------
                local function spellCB_next_only(target,caster)
                    local tool = target
                    if tool._cached_reskinname == nil then
                        tool._cached_reskinname = {}
                    end
                    tool:DoTaskInTime(0, function()
                
                        local prefab_to_skin = target.prefab_base or target.prefab
                        if target:IsValid()  then
                            local curr_skin = target.skinname
                            local userid = caster.userid or ""
                            local cached_skin = tool._cached_reskinname[prefab_to_skin]
                            local search_for_skin = cached_skin ~= nil --also check if it's owned
                            if curr_skin == cached_skin or (search_for_skin and not TheInventory:CheckClientOwnership(userid, cached_skin)) then
                                local new_reskinname = nil
                
                                if PREFAB_SKINS[prefab_to_skin] ~= nil then
                                    for _,item_type in pairs(PREFAB_SKINS[prefab_to_skin]) do
                                        if search_for_skin then
                                            if cached_skin == item_type then
                                                search_for_skin = false
                                            end
                                        else
                                            if TheInventory:CheckClientOwnership(userid, item_type) then
                                                new_reskinname = item_type
                                                break
                                            end
                                        end
                                    end
                                end
                                tool._cached_reskinname[prefab_to_skin] = new_reskinname
                                cached_skin = new_reskinname
                            end                
                            -- print(target.skinname,cached_skin)
                            TheSim:ReskinEntity( target.GUID, target.skinname, cached_skin, nil, userid )
                            if cached_skin == nil then
                                if target.components.inventoryitem then
                                    target.components.inventoryitem:ChangeImageName(target.prefab_base or target.prefab)
                                end
                            end

                        end
                    end)
                end
                local flag,error_code = pcall(spellCB_next_only,tarItem,doer)
                if flag == false then
                    print("Error : Reskin Next")
                    print(error_code)
                end

            end
            ------------------------------------------------------------------------------------------------------




        end

        function NPC_BASE_LIB:Add_Reskin_Event()
            local inst = self.inst
            inst:ListenForEvent("npc_reskin",function(inst,_table)
                if _table and _table.doer then
                    inst.npc_base_lib:Change_Item_Skin(_table.doer,inst)
                    return
                end
                inst.npc_base_lib:Change_Item_Skin(inst)
            end)    
        end

        function NPC_BASE_LIB:Equip(item)
            if self.inst.components.inventory then
                self.inst.components.inventory:Equip(item)
            else
                item:Remove()
            end
        end
        local no_equip_name_list = {
            ["wanda_npc"] = true,
            ["wormwood_npc"] = true,
            ["wortox_npc"] = true,
            ["wurt_npc"] = true,
        }
        function NPC_BASE_LIB:Equip_Random_Hat()            
            if no_equip_name_list[self.inst.prefab] == true then
                return
            end
            self.inst:ListenForEvent("equip",function(inst,_table)
                --- -- 单独处理 polly_rogershat ,海盗鹦鹉帽
                if _table and _table.item then
                    inst:DoTaskInTime(1,function()
                        if _table.item.prefab == "polly_rogershat" then
                            if _table.item.polly then
                                -- inst.npc_base_lib:Add2Child(_table.item.polly)
                                local polly = _table.item.polly
                                polly.___npc_link = inst
                                polly:DoPeriodicTask(3,function()
                                    if polly.___npc_link == nil or not polly.___npc_link:IsValid() then
                                        polly:Remove()
                                        print("npc polly_rogershat remove")
                                    end
                                end)
                                print("npc : polly_rogershat remove task added ")

                            end
                        end
                    end)
                end
            end)

            local hats = self:Get_Equipments_Hats()
            ------------------------------------------------------------------------------
            -- 【来自域外】更新的物品，不能出现在玩家获取列表里
            local newitems = { "voidclothhat","lunarplanthat","dreadstonehat"}
            for k, v in pairs(newitems) do
                table.insert(hats,v)
            end
            ------------------------------------------------------------------------------

            local ret_name = self:GetRandom(hats)
            if ret_name == "antlionhat" then
                ret_name = "strawhat"
            end
            -- if TUNING.NPC_MOD_TEST_DEBUG == true then
            --     ret_name = "polly_rogershat"
            -- end
            local temp_player = self.inst:GetPlayer()
            local item = SpawnPrefab(ret_name)
            self:Change_Item_Skin(temp_player,item)
            self:Equip(item)
            -- self:Add2Child(item)
        end

        function NPC_BASE_LIB:Equip_Random_Cloth()
            if no_equip_name_list[self.inst.prefab] == true then
                return
            end
            local Clothes = self:Get_Equipments_Body()
            ------------------------------------------------------------------------------
            -- 【来自域外】更新的物品，不能出现在玩家获取列表里
            local newitems = { "armor_lunarplant","armordreadstone","armor_voidcloth"}
            for k, v in pairs(newitems) do
                table.insert(Clothes,v)
            end
            ------------------------------------------------------------------------------
            local ret_name = self:GetRandom(Clothes)
            if ret_name == "orangeamulet" then
                ret_name = "amulet"
            end
            local temp_player = self.inst:GetPlayer()
            local item = SpawnPrefab(ret_name)
            self:Change_Item_Skin(temp_player,item)
            self:Equip(item)
            -- self:Add2Child(item)

        end

        function NPC_BASE_LIB:Equip_Random_Weapon()
            local Weapos = self:Get_Equipments_Hand()
            ------------------------------------------------------------------------------
            -- 【来自域外】更新的物品，不能出现在玩家获取列表里
            local newitems = { "sword_lunarplant","voidcloth_scythe","pickaxe_lunarplant","shovel_lunarplant","voidcloth_umbrella","staff_lunarplant"}
            for k, v in pairs(newitems) do
                table.insert(Weapos,v)
            end
            ------------------------------------------------------------------------------
            local ret_name = self:GetRandom(Weapos)
            if self.inst.prefab_base == "woodie" then
                if math.random(100) < 30 then
                    ret_name = "lucy"
                end
            end


            local temp_player = self.inst:GetPlayer()
            local item = SpawnPrefab(ret_name)
            self:Change_Item_Skin(temp_player,item)
            self:Equip(item)
            -- self:Add2Child(item)

        end

        ---------------------------------------------------------------------------------------------------------------------------
        function NPC_BASE_LIB:Get_Owner(container_flag)
            local inst = self.inst
            if inst.components.inventoryitem == nil then
                return nil
            end

            local first_owner = inst.components.inventoryitem.owner
            if first_owner == nil then
                return nil
            end
            if first_owner:HasTag("player") then
                return first_owner
            end
            if first_owner.components.container then    ------- 如果是在容器/背包里
                if first_owner.components.inventoryitem then
                    return first_owner.components.inventoryitem.owner
                else
                    if container_flag then
                        return first_owner 
                    else                            
                        return nil
                    end
                end
            end
            return nil
        end        
        ---------------------------------------------------------------------------------------------------------------------------
        function NPC_BASE_LIB:Try_2_Set_Light(arg)
            if self.inst.Light then
                self.inst.Light:Enable(arg)
            end
        end
        ---------------------------------------------------------------------------------------------------------------------------
        function NPC_BASE_LIB:Throw_Out_Items(CMD_TABLE)
            -- local _cmd_table = {
            --     target = Vector3() or Inst , or nil
            --     name = "log",
            --     num = 1,    -- default
            --     range = 8, -- default
            --     height = 3,-- default
            --     random_height = nil
            --     fn = function(item)
            -- }

            if CMD_TABLE == nil then
                return
            end

            local thePoint = nil
            if  CMD_TABLE.target == nil then
                thePoint = Vector3( self.inst.Transform:GetWorldPosition() )
            elseif CMD_TABLE.target.x then
                thePoint = CMD_TABLE.target
            elseif CMD_TABLE.target.prefab then
                thePoint = Vector3( CMD_TABLE.target.Transform:GetWorldPosition() )
            else
                return
            end

            local range = CMD_TABLE.range or 8
            local num = CMD_TABLE.num or 1
            local height = CMD_TABLE.height or 3
            local name = CMD_TABLE.name or "log"
            local random_height = CMD_TABLE.random_height or false


    
            local function SpawnItem(thePoint,ItemName,Range,throwHight)
    
                local theItem = SpawnPrefab(ItemName)
                if theItem == nil then
                    return
                end
    
                local pt = thePoint + Vector3(0,2,0)
                theItem.Transform:SetPosition(pt:Get())
                -- local down = TheCamera:GetDownVec()
                -- local angle = math.atan2(down.z, down.x) + (math.random()*60)*DEGREES
                local angle = math.random(2*PI*100)/100
                local sp = math.random(Range)
                -- theItem.Physics:SetVel(sp*math.cos(angle), math.random()*2+8, sp*math.sin(angle))
                -- SpawnPrefab("collapse_small").Transform:SetPosition(inst.Transform:GetWorldPosition())
                theItem.Physics:SetVel(sp*math.cos(angle), throwHight, sp*math.sin(angle))
                return theItem
            end
    
    
            local theSpawnItemsList ={}
            for i = 1, num, 1 do
                if random_height == true then
                    local tempItem = SpawnItem(thePoint,name,range,math.random(height))
                    if CMD_TABLE.fn then CMD_TABLE.fn(tempItem) end
                    table.insert(theSpawnItemsList,tempItem)
                else                    
                    local tempItem = SpawnItem(thePoint,name,range,height)
                    if CMD_TABLE.fn then CMD_TABLE.fn(tempItem) end
                    table.insert(theSpawnItemsList,tempItem)
                end
                -- table.insert(theSpawnItemsList,tempItem)
            end
            return theSpawnItemsList
        
        end
        function NPC_BASE_LIB:GiveItemByName(name,num)
            if type(name) ~= "string" or type(num) ~= "number" or num == 0 then
                return
            end

            local container = self.inst.components.inventory
            if container == nil then
                container = self.inst.components.container
            end

            if container == nil then
                return
            end

            if num == 1 then    ----------------------------------------- 单个物品，尝试给物品套上皮肤
                local item = SpawnPrefab(name)
                self:Change_Item_Skin(item)   
                container:GiveItem(item)
            else    ----------------------------------------------------- 多个物品，为了避免卡顿，测试能否叠加
                local stack_test_item = SpawnPrefab(name)
                if stack_test_item == nil then
                    return
                end
                if stack_test_item.components.stackable == nil then
                    for i = 1, num, 1 do
                        container:GiveItem(SpawnPrefab(name) or SpawnPrefab("log"))
                    end
                else
                    local max_size = stack_test_item.components.stackable.maxsize   -- 获取叠堆数量
                    local Rest_num = num % max_size                                 -- 不够整组的数量
                    local Group_num = math.ceil(  (num - Rest_num)/max_size   )     -- 组数
                    if Group_num > 0 then
                        for i = 1, Group_num, 1 do
                            local ret_item = SpawnPrefab(name)
                            ret_item.components.stackable.stacksize = ret_item.components.stackable.maxsize
                            container:GiveItem(ret_item)
                        end
                    end

                    if Rest_num > 0 then
                        local res_item = SpawnPrefab(name)
                        res_item.components.stackable.stacksize = Rest_num
                        container:GiveItem(res_item)
                    end
                end

                stack_test_item:Remove()
            end

        end

        function NPC_BASE_LIB:Get_Item_Stack_Max_Num(name)
            local flg,ret = pcall(function()
                                    local item = SpawnPrefab(name)
                                    if item.components.stackable == nil then
                                        item:Remove()
                                        return 1
                                    end

                                    local num = item.components.stackable.maxsize
                                    item:Remove()
                                    return num
                                end)
            if flg then
                return ret
            else
                return 1
            end
        end
        ---------------------------------------------------------------------------------------------------------------------------
        function NPC_BASE_LIB:Get_Names()
            local ret_table = self:Combine_Tables({STRINGS.CROWNAMES,STRINGS.PIGNAMES,STRINGS.SWAMPIGNAMES,STRINGS.MERMNAMES})

            local new_table = {}
            for k, v in pairs(ret_table) do
                if v and type(v) == "string" then
                    table.insert(new_table,v)
                elseif v and type(v) == "table" then
                    for kk, vv in pairs(v) do
                        if kk and type(kk) == "string" then
                            table.insert(new_table,kk)
                        elseif vv and type(vv) == "string" then
                            table.insert(new_table,vv)
                        end
                    end
                end
            end
            if #new_table > 0 then
                return new_table
            else
                return nil
            end
        end
        ---------------------------------------------------------------------------------------------------------------------------
        function NPC_BASE_LIB:Player_Killed_With_Announce(announce,source)
            local player = self.inst

            if source then
                player.npc_everything_data:Set("death_announce",{
                    announce = announce,
                    source = source,
                })
            else
                player.npc_everything_data:Set("death_announce",tostring(announce))
            end

            for i = 1, 10, 1 do
                player:DoTaskInTime((i)*0.5,function()
                    if player.components.health then
                        player.components.health:DoDelta(-321.3,nil,source)
                    end
                end)
            end
            if self.___clear_death_announce_task then
                self.___clear_death_announce_task:Cancel()
            end
            self.___clear_death_announce_task = player:DoTaskInTime(20,function()        --- 没成功杀死的话，清除语句。
                player.npc_everything_data:Set("death_announce",nil)      
                self.___clear_death_announce_task = nil          
            end)

        end
        ---------------------------------------------------------------------------------------------------------------------------
        function NPC_BASE_LIB:GetNearestPlayer(isalive)
            local temp_players_with_dis = {}
            for k, v in pairs(AllPlayers) do
                if v and v:IsValid() and v.components.playercontroller then
                    if isalive == true and not v:HasTag("playerghost") then
                        temp_players_with_dis[v] = self:Distance_Targets(self.inst,v)
                    else
                        temp_players_with_dis[v] = self:Distance_Targets(self.inst,v)                        
                    end
                end
            end

            local min_dis = 10000000000
            local ret_player = nil
            for player, dis in pairs(temp_players_with_dis) do
                if dis < min_dis then
                    ret_player = player
                end
            end
            
            return ret_player
        end
        ---------------------------------------------------------------------------------------------------------------------------
    end

    return Fx
end

return Fn